home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / admin / news / filegroup < prev    next >
Encoding:
AWK Script  |  1997-08-26  |  22.3 KB  |  687 lines

  1. #!/usr/local/bin/gawk -f
  2. # @(#) filegroup.gawk 3.2 95/01/14
  3. # 94/03/04 john h. dubois iii (john@armory.com)
  4. # 94/08/04 Added q & a options.  Bugfixes.
  5. # 94/08/07 Converted to #!awk program.
  6. # 94/08/10 Added x option.
  7. # 94/09/22 Warn about groups with status 'n'.  Added pgt options.
  8. #          Use status translation table.
  9. # 94/11/09 Added -XYNS
  10. # 94/11/16 Added multiple debug levels.  Fixed Report logic.
  11. # 94/11/21 Added rR opts, and "r" status.
  12. # 94/12/12 Made a option work again
  13. # 95/01/14 Only accept legal statuses wtih -a.
  14.  
  15. # filegroup: change status of group reception
  16. # Meaning of status:
  17. # y: spool group
  18. # n: spool group but do not allow posting
  19. # x: do not spool group
  20. # m: spool group & send postings to moderator
  21. # =: postings to group should be spooled in a different group.
  22. # This is extended with 'xm' to mean that group should not be spooled,
  23. # while retaining the information that if it is changed to spooled status it
  24. # should be moderated.
  25.  
  26. BEGIN {
  27.     Setup(Groups,VerboseState)
  28.  
  29.     while ((GlobAct || NumGroups || Change) && (ret = (getline < active)) == 1)
  30.     if (GlobAct || $1 in Groups) {
  31.         if (Report)
  32.         printf "%s %s\n",$1,VerboseState[$4]
  33.         else if (ModifyGroup($1,$2,$3,$4,NewStatus,NewActive)) {
  34.         Change = 1
  35.         if (Debug > 1)
  36.             printf "Modified specified group %s\n",$1 > "/dev/stderr"
  37.         }
  38.         NumGroups--
  39.         delete Groups[$1]
  40.     }
  41.     else if (UseNSTable) {
  42.         if (ModifyGroup($1,$2,$3,$4,NSTable,NewActive)) {
  43.         Change = 1
  44.         if (Debug > 1)
  45.             printf "Modified non-specified group %s\n",$1 \
  46.             > "/dev/stderr"
  47.         }
  48.     }
  49.     else if (!Report)    # If not just reporting
  50.         print $0 > NewActive
  51.     
  52.     if (ret == -1) {
  53.     printf "Error reading active file '%s'.  Exiting.\n",
  54.     active > "/dev/stderr"
  55.     exit 1
  56.     }
  57.  
  58.     if (NumGroups > 0) {
  59.     if (Debug)
  60.         printf "%d groups not found in active file.\n",NumElem(Groups) \
  61.         > "/dev/stderr"
  62.     if (CreationStatus != "") {
  63.         for (Group in Groups)
  64.         printf "%s 0000000000 00001 %s\n",Group,CreationStatus \
  65.         > NewActive
  66.         print \
  67.         "These groups do not exist in the active file (adding them):" \
  68.         > "/dev/stderr"
  69.         Change = 1
  70.     }
  71.     else {
  72.         print "These groups do not exist in the active file:" \
  73.         > "/dev/stderr"
  74.     }
  75.     for (Group in Groups)
  76.         print Group > "/dev/stderr"
  77.     }
  78.     if (Debug)
  79.     printf "Change=%d\n",Change > "/dev/stderr"
  80.     if (Change && !Report && !Test)
  81.     system("a=" active "; chmod 664 $a+ && chgrp news $a+ &&"\
  82.     " chown news $a+ && ln $a $a- && mv $a+ $a")
  83.     exit 0
  84. }
  85.  
  86. # Sets globals:
  87. # active    Active file name
  88. # NewActive    Name of new active file.
  89. # Quiet        Do not warn about groups that were already on.
  90. # CreationStatus    Status of groups to be added to active file.
  91. # Debug        Debugging level.
  92. # Report    Whether group status should be reported only.
  93. # NumGroups    Number of groups to process.
  94. # GlobAct    Act on all newsgroups.
  95. # Test        Do not move new active file into place.
  96. # UseNSTable    Use the NSTable for groups not specified.
  97. # NewStatus[]    Translation table from old status -> new status
  98. # NSTable[]    Translation table for groups not specified.
  99. # Groups[]    Names of groups to process.
  100. # VerboseState[]    Name for each possible state.
  101. function Setup(Groups,VerboseState,  Name,Usage) {
  102.     Name = "filegroup"
  103.     Usage = \
  104. "Usage: " Name  " [-hnxydqgt] [-d<debug-level>] [-a<status>] [-A<active-file>]\n"\
  105. "                 [-s<translation-table> [newsgroup ...]"
  106.     active = "/usr/lib/news/active"
  107.     Report = 1
  108.     # Opts normally uses -x to turn on debugging; removed that code.
  109.     ARGC = Opts(Name,Usage,"a:d>A:ghnrqs:txyXYNRS:",0)
  110.  
  111.     if ("h" in Options) {
  112.     printf \
  113. "%s: display or change status of newsgroup reception.\n"\
  114. "%s\n"\
  115. "If no flags are given, %s displays the reception status of the named\n"\
  116. "newsgroups.\n"\
  117. "If no newsgroups are named, the standard input is read for group names.\n"\
  118. "Options:\n"\
  119. "-a<status>: Add any groups that do not exist in the active file, with the\n"\
  120. "    given status (y, n, or x).  Note that this will be incorrect for any\n"\
  121. "    groups that should be moderated.\n"\
  122. "-g: Act on all groups.  No group names should be given with this option.\n"\
  123. "-A<active-file>: Use <active-file> instead of %s.\n"\
  124. "-t: Test.  Leave updated active file in active-file-name+; do not move it\n"\
  125. "    in place of the original file.\n"\
  126. "-h: Print this help.\n"\
  127. "-x: Turn off reception of the named newsgroups.  y and n are changed to x,\n"\
  128. "    m is changed to xm.\n"\
  129. "-y: Turn on reception of the named newsgroups.  x and n are changed to y,\n"\
  130. "    xm is changed to m.\n"\
  131. "-n: Mark the newsgroups as local posting disallowed.  y is changed to n.\n"\
  132. "-r: Remove the named newsgroups from the active file.\n"\
  133. "-s<translation-table>: Give a specific status translation table, in the\n"\
  134. "    form old=new[:old=new...], where each old=new pair gives a new status\n"\
  135. "    that each group with the old status should be set to.  A new status of\n"\
  136. "    'r' means that groups with the given old status will be removed from\n"\
  137. "    the active file.\n"\
  138. "-[XYNRS]: Like -[xynrs], but act on groups *not* named.  One of these can\n"\
  139. "    be used to set the \"default\" reception status at the same time that\n"\
  140. "    one of -[xynrs] is used to set a specific status for the named groups.\n"\
  141. "-d<debug-level>: Turn on debugging.  1 prints minimal debugging info;\n"\
  142. "    2 prints per-group debugging info.\n"\
  143. "-q: Do not warn about groups whose status is not changed.\n",
  144.     Name,Usage,Name,active
  145.     exit 0
  146.     }
  147.     Quiet = "q" in Options
  148.     if ((CreationStatus = Options["a"]) != "") {
  149.     if (CreationStatus !~ /^([ynmx]|xm)$/) {
  150.         printf "Bad status '%s' given with -a.  Exiting.\n",
  151.         CreationStatus > "/dev/stderr"
  152.         exit 1
  153.     }
  154.     else
  155.         Report = 0
  156.     }
  157.     if ("d" in Options)
  158.     Debug = Options["d"]
  159.     Test = "t" in Options
  160.     
  161.     if ((GlobAct = ("g" in Options)) && ARGC > 1) {
  162.     print "No group names should be given with -g.  Exiting.\n" \
  163.     > "/dev/stderr"
  164.     exit 1
  165.     }
  166.  
  167.     if ("A" in Options)
  168.     active = Options["A"]
  169.     NewActive = active "+"
  170.  
  171.     if (!GlobAct) {
  172.     if (ARGC > 1) {
  173.         for (i = 1; i < ARGC; i++)
  174.         Groups[ARGV[i]]
  175.     }
  176.     else
  177.         while (getline < "/dev/stdin" == 1)
  178.         MakeSet(Groups,$0,FS)
  179.     delete Groups[""]
  180.  
  181.     # Do not rely on the return value of MakeSet,
  182.     # since groups might be named more than once.
  183.     NumGroups = NumElem(Groups)
  184.     if (!NumGroups) {
  185.         printf "%s: No groups specified.\n",Name > "/dev/stderr"
  186.         exit 1
  187.     }
  188.     }
  189.  
  190.     InitArr(VerboseState,"x:m:xm:y:n:r","not spooled:moderated:"\
  191.     "not spooled (moderated):spooled:spooled (local posting disabled):"\
  192.     "removed from active",":")
  193.  
  194.     Report = \
  195.     MakeTable(NewStatus,"x","y","n","r","s",Options,VerboseState) && Report
  196.     UseNSTable = !MakeTable(NSTable,"X","Y","N","R","S",Options,VerboseState)
  197.     Report = !UseNSTable && Report
  198.     if (Debug)
  199.     printf "Report=%d  Test=%d  GlobAct=%d\n",Report,Test,GlobAct \
  200.     > "/dev/stderr"
  201. }
  202.  
  203. # Returns 0 if any of x y n r s is in Options, else returns 1.
  204. function MakeTable(Table,x,y,n,r,s,Options,States,  
  205. Report,Pair,Pairs,Elem,i,Type) {
  206.     Report = 1
  207.  
  208.     if (x == "X")
  209.     Type = "non-"
  210.  
  211.     if (y in Options) {
  212.     Table["x"] = "y"
  213.     Table["n"] = "y"
  214.     Table["xm"] = "m"
  215.     Report--
  216.     }
  217.     if (x in Options) {
  218.     Table["y"] = "x"
  219.     Table["n"] = "x"
  220.     Table["m"] = "xm"
  221.     Report--
  222.     }
  223.     if (n in Options) {
  224.     Table["y"] = "n"
  225.     Report--
  226.     }
  227.     if (r in Options) {
  228.     Table["y"] = "r"
  229.     Table["n"] = "r"
  230.     Table["m"] = "r"
  231.     Table["x"] = "r"
  232.     Table["xm"] = "r"
  233.     Report--
  234.     }
  235.     if (s in Options) {
  236.     split(Options[s],Pairs,":")
  237.     for (Pair in Pairs) {
  238.         if (split(Pairs[Pair],Elem,"=") != 2) {
  239.         printf "Bad format in -%s translation table.\n",
  240.         s > "/dev/stderr"
  241.         exit 1
  242.         }
  243.         if (!(Elem[1] in States) || !(Elem[2] in States)) {
  244.         printf \
  245. "Unknown state in -%s translation table; must be one of [x m xm y n r]\n",s \
  246.     > "/dev/stderr"
  247.         exit 1
  248.         }
  249.         Table[Elem[1]] = Elem[2]
  250.     }
  251.     Report--
  252.     }
  253.     if (Debug) {
  254.     printf "Status translation table for %sspecified groups:\n",
  255.     Type > "/dev/stderr"
  256.     for (i in Table)
  257.         printf "%s -> %s\n",i,Table[i] > "/dev/stderr"
  258.     }
  259.     if (Report < 0) {
  260.     printf "Error in new status for %sspecified groups:"\
  261.     " must give only one of -[%s%s%s%s].\n",Type,x,y,n,s > "/dev/stderr"
  262.     exit 1
  263.     }
  264.     return Report
  265. }
  266.  
  267. # Return value: 0 if no change, 1 if status changed.
  268. # Writes a line for group Group to file NewActive.
  269. # The first three fields of the line written are Group, c1, and c2.
  270. # The 4th field is the translation for Status.
  271. function ModifyGroup(Group,c1,c2,Status,XTable,NewActive,
  272. NewState) {
  273.     if (!(Status in XTable)) {
  274.     if (Quiet != 1)
  275.         printf "%s not modified; had status: %s\n",Group,
  276.         Status in VerboseState ? VerboseState[Status] : Status \
  277.         > "/dev/stderr"
  278.     print Group " " c1 " " c2 " " Status > NewActive
  279.     return 0
  280.     }
  281.     else {
  282.     NewState = XTable[Status]
  283.     printf "%s: %s -> %s\n",Group,VerboseState[Status],
  284.     VerboseState[NewState] > "/dev/stderr"
  285.     if (NewState != "r")
  286.         printf "%s %s %s %s\n",Group,c1,c2,NewState > NewActive
  287.     return 1
  288.     }
  289. }
  290.  
  291. # InitArr: Initialize an array with values.
  292. # Ind and Vals are separated into lists on Sep.
  293. # For each item in Ind, an index with that name is created in Arr[],
  294. # and the value with the same position in Vals is stored in it.
  295. # Global variables: none.
  296. function InitArr(Arr,Ind,Vals,sep,  numind,indnames,values) {
  297.     split(Ind,indnames,sep)
  298.     split(Vals,values,sep)
  299.     for (numind in indnames)
  300.     Arr[indnames[numind]] = values[numind]
  301. }
  302.  
  303. function ClearArr(Arr,  Elem) {
  304.     for (Elem in Arr)
  305.     delete Arr[Elem]
  306. }
  307. # Subtract the values in Subtrahend from those in Minuend
  308. function SubtractArr(Minuend,Subtrahend,  Elem) {
  309.     for (Elem in Subtrahend)
  310.     Minuend[Elem] -= Subtrahend[Elem]
  311. }
  312.  
  313. # For each element of the array In, an element is created in Out having
  314. # an index equal to the value of the element in In and a value equal to 
  315. # the index of the element in In.
  316. function Invert(In,Out,Index) {
  317.     for (Index in In)
  318.     Out[In[Index]] = Index
  319. }
  320.  
  321. # Assign: make an array from a list of assignments.
  322. # An index with the name of each variable in the list is created in the array.
  323. # Its value is set to the value given for the 
  324. # Input variables: 
  325. # Elements is a string containing the list of variable-value pairs.
  326. # Sep is the string that separates the pairs in the list.
  327. # AssignOp is the string that separates variables from values.
  328. # Output variables:
  329. # Arr is the array.
  330. # Return value: the number of elements added to the set.
  331. # Example:
  332. # Assign(Arr,"foo=blot bar=blat baz=blit"," ","=")
  333. function Assign(Arr,Elements,Sep,AssignOp,
  334. Num,Names,Elem,Assignments,Assignment) {
  335.     Num = split(Elements,Assignments,Sep)
  336.     for (; Num; Num--) {
  337.     Assignment = Assignments[Num]
  338.     Ind = index(Assignment,AssignOp)
  339.     Arr[substr(Assignment,1,Ind - 1)] = substr(Assignment,Ind + 1)
  340.     }
  341.     return Num
  342. }
  343.  
  344. # Returns 1 if Set is empty, 0 if not.
  345. function IsEmpty(Set,  i) {
  346.     for (i in Set)
  347.     return 0
  348.     return 1
  349. }
  350.  
  351. # MakeSet: make a set from a list.
  352. # An index with the name of each element of the list
  353. # is created in the given array.
  354. # Input variables: 
  355. # Elements is a string containing the list of elements.
  356. # Sep is the character that separates the elements of the list.
  357. # Output variables:
  358. # Set is the array.
  359. # Return value: the number of elements added to the set.
  360. function MakeSet(Set,Elements,Sep,  i,Num,Names) {
  361.     Num = split(Elements,Names,Sep)
  362.     for (i = 1; i <= Num; i++)
  363.     Set[Names[i]]
  364.     return Num
  365. }
  366.  
  367. # Returns the number of elements in set Set
  368. function NumElem(Set,  elem,Num) {
  369.     for (elem in Set)
  370.     Num++
  371.     return Num
  372. }
  373.  
  374. # Note: removed the code that treats -x specially from InitOpts()
  375. # so that -x can be used as a regular option for filegroup.
  376.  
  377. # @(#) ProcArgs 1.2.2 94/09/23
  378. # 92/02/29 john h. dubois iii (john@armory.com)
  379. # 93/07/18 Added "#" arg type
  380. # 93/09/26 Do not count -h against MinArgs
  381. # 94/01/01 Stop scanning at first non-option arg.  Added ">" option type.
  382. #          Removed meaning of "+" or "-" by itself.
  383. # 94/03/08 Added & option and *()< option types.
  384. # 94/04/02 Added NoRCopt to Opts()
  385. # 94/06/11 Mark numeric variables as such.
  386. # 94/07/08 Opts(): Don't require any args if 'h' option is given.
  387. # 94/09/23 Fixed bug that caused fail if -opt<value> given as last arg.
  388.  
  389. # optlist is a string which contains all of the possible command line options.
  390. # A character followed by certain characters indicates that the option takes
  391. # an argument, with type as follows:
  392. # :    String argument
  393. # *    Floating point argument
  394. # (    Non-negative floating point argument
  395. # )    Positive floating point argument
  396. # #    Integer argument
  397. # <    Non-negative integer argument
  398. # >    Positive integer argument
  399. # The only difference the type of argument makes is in the runtime argument
  400. # error checking that is done.
  401.  
  402. # The & option is a special case used to get numeric options without the
  403. # user having to give an option character.  It is shorthand for [-+.0-9].
  404. # If & is included in optlist and an option string that begins with one of
  405. # these characters is seen, the value given to "&" will include the first
  406. # char of the option.  & must be followed by a type character other than ":".
  407. # Note that if e.g. &> is given, an option of -.5 will produce an error.
  408.  
  409. # Strings in argv[] which begin with "-" or "+" are taken to be
  410. # strings of options, except that a string which consists solely of "-"
  411. # or "+" is taken to be a non-option string; like other non-option strings,
  412. # it stops the scanning of argv and is left in argv[].
  413. # An argument of "--" or "++" also stops the scanning of argv[] but is removed.
  414. # If an option takes an argument, the argument may either immediately
  415. # follow it or be given separately.
  416. # "-" and "+" options are treated the same.  "+" is allowed because most awks
  417. # take any -options to be arguments to themselves.  gawk 2.15 was enhanced to
  418. # stop scanning when it encounters an unrecognized option, though until 2.15.5
  419. # this feature had a bug that caused problems in some cases.
  420.  
  421. # If an option that does not take an argument is given,
  422. # an index with its name is created in Options and its value is set to "1".
  423. # If an option that does take an argument is given,
  424. # an index with its name is created in Options and its value
  425. # is set to the value of the argument given for it.
  426. # Options and their arguments are deleted from argv.
  427. # Note that this means that there may be gaps left in the indices of argv[].
  428. # If compress is nonzero, argv[] is packed by moving its elements so that
  429. # they have contiguous integer indices starting with 0.
  430. # argv[0] is not examined.
  431. # The number of arguments left in argc is returned.
  432. # If an error occurs, the global string OptErr is set to an error message
  433. # and -1 is returned.
  434. function ProcArgs(argc,argv,OptList,Options,compress,
  435. ArgNum,ArgsLeft,Arg,ArgLen,ArgInd,Option,Pos,NumOpt,Value,HadValue,
  436. NeedNextOpt,GotValue)
  437. {
  438. # ArgNum is the index of the argument being processed.
  439. # ArgsLeft is the number of arguments left in argv.
  440. # Arg is the argument being processed.
  441. # ArgLen is the length of the argument being processed.
  442. # ArgInd is the position of the character in Arg being processed.
  443. # Option is the character in Arg being processed.
  444. # Pos is the position in OptList of the option being processed.
  445. # NumOpt is true if a numeric option may be given.
  446.     ArgsLeft = argc
  447.     NumOpt = index(OptList,"&")
  448.     for (ArgNum = 1; ArgNum < argc; ArgNum++) {
  449.     if ((Arg = argv[ArgNum]) !~ /^[-+]./)    # Not an option; quit
  450.         break
  451.     delete argv[ArgNum]
  452.     ArgsLeft--
  453.     if ((Arg == "--") || (Arg == "++"))
  454.         break
  455.     ArgLen = length(Arg)
  456.     for (ArgInd = 2; ArgInd <= ArgLen; ArgInd++) {
  457.         Option = substr(Arg,ArgInd,1)
  458.         if (NumOpt && Option ~ /[-+.0-9]/) {
  459.         Option = "&"
  460.         Arg = "&" Arg
  461.         ArgLen++
  462.         Pos = NumOpt
  463.         }
  464.         else if (!(Pos = index(OptList,Option)) || Option == "&") {
  465.         OptErr = "Invalid option: -" Option
  466.         return -1
  467.         }
  468.  
  469.         # Find what the value of the option will be if it needs one
  470.         # NeedNextOpt is true if the option specifier is the last char of
  471.         # this arg, which means that if the option requires a value it is
  472.         # the next arg.
  473.         if (NeedNextOpt = (ArgInd >= ArgLen)) { # Value is the next arg
  474.         if (GotValue = ArgNum + 1 < argc)
  475.             Value = argv[ArgNum+1]
  476.         }
  477.         else {    # Value is included with option
  478.         Value = substr(Arg,ArgInd + 1)
  479.         GotValue = 1
  480.         }
  481.  
  482.         if (HadValue = AssignVal(Option,Value,Options,
  483.         substr(OptList,Pos + 1,1),GotValue)) {
  484.         if (HadValue == -1)
  485.             return -1
  486.         if (NeedNextOpt) {
  487.             delete argv[++ArgNum]
  488.             ArgsLeft--
  489.         }
  490.         break    # Used up this option
  491.         }
  492.     }
  493.     }
  494.     if (compress != 0)
  495.     PackArr(argv,ArgsLeft)
  496.     return ArgsLeft
  497. }
  498.  
  499. # Global variables: OptErr
  500. # Return value: -1 on error, 0 if option did not require an argument,
  501. # 1 if it did.
  502. function AssignVal(Option,Value,Options,ArgType,GotValue,Name,
  503. UsedValue,Err) {
  504.     # If option takes a value...
  505.     if (UsedValue = (ArgType ~ "[:*()#<>]")) {
  506.     if (!GotValue) {
  507.         if (Name != "")
  508.         OptErr = "Variable requires a value -- " Name
  509.         else
  510.         OptErr = "option requires an argument -- " Option
  511.         return -1
  512.     }
  513.     if ((Err = CheckType(ArgType,Value,Option,Name)) != "") {
  514.         OptErr = Err
  515.         return -1
  516.     }
  517.     # Mark this as a numeric variable; will be propogated to Options[] val.
  518.     if (ArgType != ":")
  519.         Value += 0
  520.     }
  521.     else
  522.     Value = 1
  523.     if (!(Option in Options))    # Do not overwrite previously assigned values
  524.     Options[Option] = Value
  525.     return UsedValue
  526. }
  527.  
  528. # Option is the option letter
  529. # Value is the value being assigned
  530. # Name is the var name of the option, if any
  531. # ArgType is one of:
  532. # :    String argument
  533. # *    Floating point argument
  534. # (    Non-negative floating point argument
  535. # )    Positive floating point argument
  536. # #    Integer argument
  537. # <    Non-negative integer argument
  538. # >    Positive integer argument
  539. # Returns null on success, err string on error
  540. function CheckType(ArgType,Value,Option,Name,  Err) {
  541.     if (ArgType == ":")
  542.     return ""
  543.     # A number begins with option + or -, and is followed by a string of
  544.     # digits or a decimal with digits before it, after it, or both
  545.     if (Value !~ /^[-+]?([0-9]+|[0-9]+?\.[0-9]+|[0-9]+\.)$/)
  546.     Err = "must be a number"
  547.     else if (ArgType ~ "[#<>]" && Value ~ /\./)
  548.     Err = "may not include a fraction"
  549.     else if (ArgType ~ "[()<>]" && Value < 0)
  550.     Err = "may not be negative"
  551.     else if (ArgType ~ "[)>]" && Value == 0)
  552.     Err = "must be a positive number"
  553.     if (Err != "") {
  554.     if (Name != "")
  555.         return "Value assigned to variable " Name " " Err
  556.     else {
  557.         if (Option == "&")
  558.         Option = Value
  559.         return "Value assigned to option -" Option " " Err
  560.     }
  561.     }
  562.     else
  563.     return ""
  564. }
  565.  
  566. # Packs Arr to indices starting with 0
  567. # Num should be the number of elements in Arr
  568. function PackArr(Arr,Num,  NewInd,OldInd) {
  569.     NewInd = OldInd = 0
  570.     for (; Num; Num--) {
  571.     while (!(OldInd in Arr))
  572.         OldInd++
  573.     if (NewInd != OldInd) {
  574.         Arr[NewInd] = Arr[OldInd]
  575.         delete Arr[OldInd]
  576.     }
  577.     OldInd++
  578.     NewInd++
  579.     }
  580. }
  581.  
  582. # Note: only the above functions are needed by ProcArgs.
  583. # The rest of these functions call ProcArgs() and also do other
  584. # option-processing stuff.
  585.  
  586. # Opts: Process command line arguments.
  587. # Opts processes command line arguments using ProcArgs()
  588. # and checks for errors.  If an error occurs, a message is printed
  589. # and the program is exited.
  590. #
  591. # Input variables:
  592. # Name is the name of the program, for error messages.
  593. # Usage is a usage message, for error messages.
  594. # OptList the option description string, as used by ProcArgs().
  595. # MinArgs is the minimum number of non-option arguments that this
  596. # program should have, non including ARGV[0] and +h.
  597. # If the program does not require any non-option arguments,
  598. # MinArgs should be omitted or given as 0.
  599. # rcFile, if given, is the name of a file to read for variable initialization.
  600. # Values given in it will not override values given on the command line.
  601. # VarNames is a comma-separated list of variable names to map to options,
  602. # in the same order as the options are given in OptList.
  603. # If UseEnv is given and nonzero, the variables will also be searched for in
  604. # the environment.  Values given in the environment will override those given
  605. # in the rcfile but not those given on the command line.
  606. # NoRCopt, if given, is an additional letter option that if given on the
  607. # command line prevents the rcfile from being read.
  608. # Special options:
  609. # If x is made an option and is given, some debugging info is output.
  610. # h is assumed to be the help option.
  611.  
  612. # Global variables:
  613. # The command line arguments are taken from ARGV[].
  614. # The arguments that are option specifiers and values are removed from
  615. # ARGV[], leaving only ARGV[0] and the non-option arguments.
  616. # The number of elements in ARGV[] should be in ARGC.
  617. # After processing, ARGC is set to the number of elements left in ARGV[].
  618. # The option values are put in Options[].
  619. # On error, Err is set to 1 so it can be checked for in an END block.
  620.  
  621. # Return value: The number of elements left in ARGV is returned.
  622.  
  623. function Opts(Name,Usage,OptList,MinArgs,rcFile,VarNames,UseEnv,NoRCopt,
  624. ArgsLeft) {
  625.     if (MinArgs == "")
  626.     MinArgs = 0
  627.     ArgsLeft = ProcArgs(ARGC,ARGV,OptList NoRCopt,Options,1)
  628. #    if ((ArgsLeft + ("h" in Options)) < (MinArgs+1)) {
  629.     if (ArgsLeft < (MinArgs+1) && !("h" in Options)) {
  630.     if (ArgsLeft != -1)
  631.         OptErr = "Not enough arguments"
  632.     print Name ": " OptErr ".  Use -h for help."
  633.     print Usage
  634.     Err = 1
  635.     exit 1
  636.     }
  637.     if (rcFile != "" && (NoRCopt == "" || !(NoRCopt in Options)) &&
  638.     InitOpts(rcFile,Options,OptList,VarNames,UseEnv) == -1)
  639.     {
  640.     print Name ": " OptErr ".  Use -h for help."
  641.     Err = 1
  642.     exit 1
  643.     }
  644.     return ArgsLeft
  645. }
  646.  
  647. # Global vars: sets OptErr; uses ENVIRON[];
  648. # if anything is read from the rcFile, sets READ_RCFILE to 1
  649. function InitOpts(rcFile,Options,OptTypes,VarNames,UseEnv,
  650. Line,Var,Pos,Vars,Map,CharOpt,NumVars,TypesInd,Types,Type,Ret) {
  651.     NumVars = split(VarNames,Vars,",")
  652.     TypesInd = Ret = 0
  653.     for (i = 1; i <= NumVars; i++) {
  654.     Var = Vars[i]
  655.     CharOpt = substr(OptTypes,++TypesInd,1)
  656.     if (CharOpt ~ "^[:*()#<>&]$")
  657.         CharOpt = substr(OptTypes,++TypesInd,1)
  658.     Map[Var] = CharOpt
  659.     Types[Var] = Type = substr(OptTypes,TypesInd+1,1)
  660.     # Do not overwrite entries from environment
  661.     if (UseEnv && Var in ENVIRON && AssignVal(CharOpt,ENVIRON[Var],Options,
  662.     Type,1,Var) == -1)
  663.         return -1
  664.     }
  665.     if (rcFile ~ "^~/")
  666.     rcFile = ENVIRON["HOME"] substr(rcFile,2)
  667.     while ((getline Line < rcFile) == 1) {
  668.     READ_RCFILE = 1
  669.     if (Line !~ /^#/ && Line !~ "^[ \t]*$") {
  670.         if (Pos = index(Line,"="))
  671.         Var = substr(Line,1,Pos-1)
  672.         else
  673.         Var = Line    # If no value, var is entire line
  674.         if (Var in Map) {
  675.         if (AssignVal(Map[Var],substr(Line,Pos+1),Options,
  676.         Types[Var],Pos != 0,Var) == -1)
  677.             return -1
  678.         }
  679.         else {
  680.         OptErr = sprintf("Unknown var \"%s\" set in %s",Var,rcFile)
  681.         Ret = -1
  682.         }
  683.     }
  684.     }
  685.     return Ret
  686. }
  687.